package embeded.impl;

import interfaces.Subscriber;
import interfaces.SubscriberPublisherManager;
import interfaces.impl.SubscriberPublisherManagerImpl;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.rmi.RemoteException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;

import operations.OperationType;
import operations.Replicated;
import operations.impl.OperationFactory;

import org.omg.CORBA.ORB;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import topic.Topic;
import topic.impl.TopicImpl;

import accessers.DataReader;
import agent.AgentDCS;
import agent.impl.AgentDCSImpl;
import data.DataObject;
import embeded.EmbededObject;
import embeded.EmbededObjectListener;
import embeded.Participant;
import embeded.ParticipantLisntener;

/**
 * 
 * @author Doumbouya
 * 
 */
public class EmbededObjectImpl implements EmbededObject {
	private String name = null;
	private Vector<EmbededObject> embededObjectsNeighbor = null;
	private Topic topic = null;
	private Vector<Participant> participants = null;
	private SubscriberPublisherManager subscriber_publisher_manager = null;
	private AgentDCS agentDCS = null;
	private EmbededObjectListener embededObjectListener = null;
	private ParticipantLisntener participantListener = null;

	public ORB orb = null;
	private POA poa = null;
	private int listening_port;
	private String listening_address = null;
	private boolean isLoad = false;

	public JTextArea textfiel = new JTextArea();
	public JPanel contextPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));

	public EmbededObjectImpl(String name, ORB orb, POA poa)
			throws RemoteException {
		this.name = name;
		this.orb = orb;
		this.poa = poa;

		embededObjectsNeighbor = new Vector<EmbededObject>();
		participants = new Vector<Participant>();
		subscriber_publisher_manager = new SubscriberPublisherManagerImpl();
		textfiel.append(name + " is load successfully\n");

		// creation du root topic
		initRootTopic();
		initFrame();
		showFrame();
		embededObjectListener = new EmbededObjectListenerImpl(this, this.orb,
				this.poa);
		embededObjectListener.startListening();
		participantListener = new ParticipantLisntenerImpl(this, this.orb,
				this.poa);
		participantListener.startListening();
	}

	private void initRootTopic() throws RemoteException {
		topic = new TopicImpl("root", "", poa, new DataObject(orb.create_any()));
		topic.getDataObject().setString("root");
		topic.setEmbededObject(this);
		topic.setSuscriber(getInterfaceManager().addNewDataReaderListener(
				topic.getFullName(), topic.getDataReader()));
	}

	public void broadcast() throws RemoteException {
		try {
			// servant
			_EmbededObjectImpl_Tie tie = new _EmbededObjectImpl_Tie();
			tie.setTarget(this);

			org.omg.CORBA.Object objet = poa.servant_to_reference(tie);
			String ior = orb.object_to_string(objet);

			loadProperties();
			InetAddress group = InetAddress.getByName(listening_address);
			DatagramSocket socket = new DatagramSocket();
			DatagramPacket writter = new DatagramPacket(ior.getBytes(),
					ior.getBytes().length, group, listening_port);
			socket.send(writter);
			socket.close();
			textfiel.append("Broadcast sussesfull\n");
		} catch (Exception e) {
			textfiel.append("Error in broadcasting\n");
			e.printStackTrace();
		}
	}

	private boolean loadProperties() {
		Properties prop = new Properties();
		String path = new File(".").getAbsolutePath();
		path = path.substring(0, path.length() - 1);
		try {
			prop.load(new FileInputStream(path + "conf\\properties.properties"));
			listening_port = Integer.parseInt(prop
					.getProperty("EMBEDED_LISTENING_PORT"));
			listening_address = prop.getProperty("EMBEDED_LISTENING_ADDRESS");
		} catch (Exception e) {
			textfiel.append("Error getting properties from file\n");
			e.printStackTrace();
			return false;
		}
		return true;
	}

	public void registerEmbededObjects(EmbededObject embededObject, Topic topic)
			throws RemoteException {
		embededObjectsNeighbor.add(embededObject);
		if (!isLoad) {
			Topic t = topic;
			if (t != null) {
	//			System.out.println("sdf");
				loadTopic(t);
			} else {
	//			System.out.println("sdfdsqdkj");
			}
			isLoad = true;
		}
		textfiel.append("Registring " + embededObject.getName() + "\n");
	}

	private void loadTopic(Topic topic) {
		this.topic = topic;
		try {
			showFrame();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*Topic t, t1;
		try {
			t = new TopicImpl(getName(), topic.getFullName().substring(0,
					topic.getFullName().lastIndexOf(".") + 1), poa);
			t.setDataObject(topic.getDataObject());
			for (int i = 0; i < topic.getChildreenCount(); i++) {
				t1 = topic.getChildreen().get(
						topic.getChildreen().keySet().toArray()[i]);
				t.addTopic(t1.getName(), t1.getAllTopic(), true);
			}
			this.topic = t;
			this.topic.initAllTopic(this);
		} catch (RemoteException e1) {
			e1.printStackTrace();
		}*/
	}

	public Topic getRootTopic() throws RemoteException {
		return topic;
	}

	public Topic getAllTopic() throws RemoteException {
		Topic t = topic.getAllTopic();
		return t;
	}

	public DataObject getData(String topicFullName) throws RemoteException {
		if (topicFullName.equals(""))
			return null;
		if (topicFullName.equals("root"))
			return topic.getDataObject();
		Topic resultTopic = this.topic.contain(topicFullName);
		if (resultTopic != null)
			return resultTopic.getDataObject();
		return null;
	}

	public void setData(String topicFullName, DataObject data)
			throws RemoteException {
		if (topicFullName.equals(""))
			return;
		if (topicFullName.equals("root"))
			topic.setDataObject(data);
		Topic resultTopic = this.topic.contain(topicFullName);
		if (resultTopic != null)
			resultTopic.setDataObject(data);
	}

	public boolean addTopic(String parentTopicName, Topic topic)
			throws RemoteException {
		if (!parentTopicName.equals("")) {
			if (parentTopicName.equals("root")) {
				this.topic.addTopic(topic.getName(), topic, false);
				return true;
			}
			Topic parentTopic = this.topic.contain(parentTopicName);
			if (parentTopic != null) {
				parentTopic.addTopic(topic.getName(), topic, false);
				return true;
			}
		}
	//	System.out.println("3");
		return false;
	}

	public void removeTopic(String topicFullName) throws RemoteException {
		if (topicFullName.equals("") || topicFullName.equals("root"))
			return;
		Topic resultTopic = this.topic.contain(topicFullName);
		if (resultTopic != null) {
			resultTopic = this.topic.contain(topicFullName.substring(0,
					topicFullName.lastIndexOf(".")));
			resultTopic
					.deleteTopic(topicFullName.substring(
							topicFullName.lastIndexOf(".") + 1,
							topicFullName.length()));
		}
	}

	public boolean createTopic(String topicParentName, String topicName)
			throws RemoteException {
		activateAgent();
		OperationFactory factory = new OperationFactory();
		Topic new_Topic = agentDCS.createTopic(topicParentName, factory
				.buildOperation("topic", new OperationType(
						OperationType._create), new Replicated(
						Replicated.isReplicated), topicName));
		if (new_Topic == null)
			return false;
		addTopic(topicParentName, new_Topic);
		return true;
	}

	public Participant addParticipant(String topicFullName)
			throws RemoteException {
		Topic resultTopic = this.topic.contain(topicFullName);
		if (resultTopic != null) {
			Participant newParticipant = new ParticipantImpl(
					resultTopic.getFullName(), resultTopic);
			participants.add(newParticipant);
			this.textfiel.append("loadding participant  : " + topicFullName
					+ "\n");
			return newParticipant;
		}
		return null;
	}

	public void removeParticipant(String topicFullName) throws RemoteException {
		participants.remove(topicFullName);
		this.textfiel.append("unloadding participant  : " + topicFullName
				+ "\n");
	}

	public void refreshParticipants(String participantName)
			throws RemoteException {
		DataObject data = null;
		for (int i = 0; i < participants.size(); i++) {
			if (participants.get(i).getName().equals(participantName)) {
				participants.get(i).refresh();
				data = participants.get(i).getData();
			}
		}
		if (data != null) {
			for (int i = 0; i < embededObjectsNeighbor.size(); i++) {
				embededObjectsNeighbor.get(i).setData(participantName, data);
				embededObjectsNeighbor.get(i).refreshParticipants(
						participantName);
			}
		}
	}

	public AgentDCS activateAgent() throws RemoteException {
		if (agentDCS == null) {
			agentDCS = new AgentDCSImpl();
			agentDCS.broadcast();
		}
		return agentDCS;
	}

	public AgentDCS getAgent() throws RemoteException {
		return agentDCS;
	}

	public SubscriberPublisherManager getInterfaceManager()
			throws RemoteException {
		return subscriber_publisher_manager;
	}

	public boolean equalsEmbeded(EmbededObject embeded) throws RemoteException {
		return this.getName().equals(embeded.getName());
	}

	public boolean stopAll() throws RemoteException {
		embededObjectListener.stopListening();
		return true;
	}

	public String getName() throws RemoteException {
		return this.name;
	}

	public int getParticipantCount() throws RemoteException {
		return participants.size();
	}

	public int getNeighborCount() throws RemoteException {
		return embededObjectsNeighbor.size();
	}

	public void initFrame() {
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(600, 400);
		frame.setLayout(new BorderLayout());
		frame.setTitle(name);

		JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		split.add(textfiel);
		split.add(contextPanel);
		split.setDividerLocation(300);

		frame.getContentPane().add(split);
		frame.setVisible(true);
	}

	public void showFrame() throws RemoteException {
		DefaultMutableTreeNode node = topic.createNode();

		JTree tree = new JTree(node);
		if (contextPanel.getComponentCount() != 0)
			contextPanel.remove(contextPanel.getComponentCount() - 1);

		contextPanel.invalidate();
		contextPanel.add(tree);
		for (int i = 0; i < tree.getRowCount(); i++) {
			tree.expandRow(i);
		}
		contextPanel.validate();

		contextPanel.repaint();
	}

	public JTextArea getTextfield() throws RemoteException {
		return textfiel;
	}

}
